package com.wang.lottery.domain.strategy.service.algorithm;

import com.wang.lottery.domain.strategy.model.vo.AwardRateInfo;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author wjh
 * @create 2023-11-01 14:52
 * 公用的算法逻辑，就相当于模板设计模式
 */
public abstract class BaseAlgorithm implements IDrawAlgorithm{
    //定义一个斐波那契散列增量
    private final int HASH_INCREMENT = 0x61c88647;
    //初始化数组长度
    private final int RATE_TUPLE_LENGTH =128;
    //定义由抽奖策略id:奖品id数组组成map集合，这是给单项概率算法用的
    protected Map<Long,String[]> rateTupleMap = new ConcurrentHashMap<>();
    //定义由奖品策略id:奖品信息列表组成的map集合，这是给整体概率算法用的
    protected Map<Long,List<AwardRateInfo>> awardRateInfoMap = new ConcurrentHashMap<>();
    /**
     * 初始化抽奖池
     * @param strategyId        策略ID
     * @param awardRateInfoList 奖品概率配置集合 「值示例：AwardRateInfo.awardRate = 0.04」
     */
    @Override
    public void initRateTuple(Long strategyId, List<AwardRateInfo> awardRateInfoList) {
        //整体概率的初始化直接赋值就行
        awardRateInfoMap.put(strategyId,awardRateInfoList);
        
        //单项概率的初始化
        //此方法首先判断缓存MAP中是否存在指定key的值，如果存在就返回这个key对于的value数据，如果不存在，会自动调用mappingFunction(key)计算key的value，然后将key = value放入到缓存Map。
        //说白就是初始化只用给key，也就是strategyId，然后按照k -> new String[RATE_TUPLE_LENGTH]生成一个数组，这个数组就是value并返回它
        String[] awardIds = rateTupleMap.computeIfAbsent(strategyId,id -> new String[RATE_TUPLE_LENGTH]);
        //secureRandom.nextInt(100)的范围是[0，100），+1就成了[1，100]
        //所以不能从0开始填充，因为随机数不会出现0，如果奖品填充到0，那么这个奖品永远不会被抽到
        AtomicInteger cursorVal = new AtomicInteger(0);
        awardRateInfoList.forEach(awardRateInfo -> {
            //获取奖品概率x100
            int intValue = awardRateInfo.getAwardRate().multiply(new BigDecimal(100)).intValue();
            // 循环填充概率范围值
            // 计算数组索引并填充数据
            //如果第一个奖品中奖概率是20，那么前20次循环都是在填充该奖品，以此类推
            for (int i = cursorVal.get() + 1; i <= (intValue + cursorVal.get()); i++) {
                awardIds[hashIdx(i)] = awardRateInfo.getAwardId();
            }
            cursorVal.addAndGet(intValue);
        });
    }

    

    /**
     * 斐波那契散列哈希，计算哈希索引下标值
     * @param val 值
     * @return 索引
     */
    public int hashIdx(int val) {
        int index = val * HASH_INCREMENT + HASH_INCREMENT;
        return index & RATE_TUPLE_LENGTH - 1;
    }
}
